package jass.generators;
import jass.engine.*;
import java.net.*;

/**
   A force model based on looping through a buffer, loaded from an audio
   file/url or provided by caller.
   @author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class LoopBuffer extends Out {
    /** Buffer to loop */
    protected float[] loopBuffer;

    /** Buffer length */
    protected int loopBufferLength;

    /** Playback volume. */
    protected float volume = 1;

    /** Loop speed through buffer in seconds per second. */
    protected float speed = 1;

    /** Current fractional position [0 1] of pointer in buffer. */
    protected float x = 0;

    /** Current integer position of pointer in buffer. */
    protected int ix = 0;
    
    /**  Current  fractional speed [0  1]  of pointer  in  buffer  per sample. */
    protected float dx;
    
    /**  Current  integer speed of pointer  in  buffer  per sample. */
    protected int dix;

    /** Sampling rate in Hertz of Out. */
    public float srate;

    /** Sampling rate ratio, srateLoopBuffer/srate */
    private float srateRatio = 1;

    /** Sampling rate in Hertz of loaded buffer. */
    public float srateLoopBuffer;

    private String name = "x";

    /**
       For derived classes
       @param bufferSize biffer size
     */
    public LoopBuffer(int bufferSize) {
        super(bufferSize); // this is the internal buffer size
    }
    
    /** Construct loop force from named file.
        @param srate sampling rate in Hertz.
        @param bufferSize bufferSize of this Out
        @param fn Audio file name.
    */
    public LoopBuffer(float srate,int bufferSize, String fn) {
        super(bufferSize); // this is the internal buffer size
        AudioFileBuffer afBuffer = new AudioFileBuffer(fn);
        loopBuffer = afBuffer.buf;
        loopBufferLength = loopBuffer.length;
        srateLoopBuffer = afBuffer.srate;
        this.srate = srate;
        srateRatio = srateLoopBuffer/srate;
        setSpeed(speed);
        this.name = fn;
        /*
          System.out.println(name);
          for(int i=0;i<loopBuffer.length;i++) {
          System.out.println(loopBuffer[i]);
          }
        */
    }

        
    /** Construct loop force from named URL.
        @param srate sampling rate in Hertz.
        @param bufferSize bufferSize of this Out
        @param url Audio file url name.
    */
    public LoopBuffer(float srate,int bufferSize, URL url) {
        super(bufferSize); // this is the internal buffer size
        AudioFileBuffer afBuffer = new AudioFileBuffer(url);
        loopBuffer = afBuffer.buf;
        loopBufferLength = loopBuffer.length;
        srateLoopBuffer = afBuffer.srate;
        this.srate = srate;
        srateRatio = srateLoopBuffer/srate;
        setSpeed(speed);
        this.name = url.toString();
    }

    /** Construct loop force and provide buffer at same sampling rate.
        @param srate sampling rate in Hertz.
        @param bufferSize bufferSize of this Out.
        @param loopBuffer looping buffer.
    */
    public LoopBuffer(float srate,int bufferSize, float[] loopBuffer) {
        super(bufferSize); // this is the internal buffer size
        this.loopBuffer = loopBuffer;
        srateLoopBuffer = this.srate = srate;
        loopBufferLength = loopBuffer.length;
        setSpeed(speed);
    }

    /** Get the loopbuffer as array.
        @return The containing loopbuffer
    */
    public float[] getLoopBuffer() {
        return loopBuffer;
    }
    
    /** Set force magnitude.
        @param val Volume.
    */
    public void setVolume(float val) {
        //System.out.println(name+" volume= "+val);
        volume = val;
    }

    /** Set loopspeed.
        @param speed Loop speed., 1 corresponding to original recorded speed.
    */
    public void setSpeed(float speed) {
        this.speed = speed;
        float tmp = speed * srateRatio;
        dix = (int)tmp;
        dx = tmp - dix;
    }

    /** Get next sample value, interpolating in between sample points.
     */
    protected float getNextSample() {
        ix += dix;
        x += dx;
        if(x > 1.f) {
            x -= 1.f;
            ix++;
        }
        while(ix >= loopBufferLength) {
            ix -= loopBufferLength;
        }
        int next_index;
        if(ix == loopBufferLength-1) {
            next_index = 0;
        } else {
            next_index = ix+1;
        }
        float val = (1.f - dx) * loopBuffer[ix] + dx * loopBuffer[next_index];
        return (float)(val * volume);
    }

    /** Compute the next buffer.
     */
    public void computeBuffer() {
        int bufsz = getBufferSize();
        for(int k=0;k<bufsz;k++) {
            buf[k] = getNextSample();
        }
    }

}


